#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/file.h>

/*打开串口dev文件*/
int open_dev(const char * Dev)
{
    int fd = 0;
    fd = open(Dev, O_RDWR|O_NOCTTY| O_NDELAY);
    if (fd == -1) {
        perror("open COM error");
        return -1;
    }

    // 加锁文件
    if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
        perror("flock error");
        close(fd);
        return -1;
    }

    return fd;
}

/*关闭串口*/
int close_dev(int fd)
{
    // 解锁文件
    if (flock(fd, LOCK_UN) == -1) {
        perror("flock unlock error");
        close(fd);
        return -1;
    }
    if (fd != -1) {
        close(fd);
    }
    return 0;
}


/*设置波特率*/
int set_baudrate(int fd, int baudrate)
{
    int status = 0;
    speed_t baudrate_t;
    struct termios option = {0};

    switch (baudrate) {
        case 300:
            baudrate_t = B300;
            break;
        case 1200:
            baudrate_t = B1200;
            break;
        case 2400:
            baudrate_t = B2400;
            break;
        case 4800:
            baudrate_t = B4800;
            break;
        case 9600:
            baudrate_t = B9600;
            break;
        case 19200:
            baudrate_t = B19200;
            break;
        case 38400:
            baudrate_t = B38400;
            break;
        
        case 115200:
            baudrate_t = B115200;
            break;
        case 230400:
            baudrate_t = B230400;
            break;
        case 460800:
            baudrate_t = B460800;
            break;
        case 500000:
            baudrate_t = B500000;
            break;
        case 921600:
            baudrate_t = B921600;
            break;
        default:
            baudrate_t = B9600;
            break;
    }
    tcgetattr(fd, &option);
    tcflush(fd, TCIOFLUSH);
    cfsetispeed(&option, baudrate_t);
    cfsetospeed(&option, baudrate_t);
    status = tcsetattr(fd, TCSANOW, &option);
    if(status != 0) {       
        perror("tcsetattr COM error");
        return -1;
    }

    tcflush(fd, TCIOFLUSH);
    return 0;
}


int set_other_param(int fd, int databits, int stopbits, int parity)
{
    struct termios option = {0};
    if(tcgetattr(fd, &option) != 0) {       
        perror("tcsetattr COM error");
        return -1;
    }

    option.c_cflag &= ~CSIZE;

    
    switch (databits) {
        case 7:
            option.c_cflag |= CS7;
            break;
        case 8:
            option.c_cflag |= CS8;
            break;
        
        default:
            fprintf(stderr, "Unsupported data size\n");
            return -1;
            break;
        }
    switch (stopbits) {
        case 1:
            option.c_cflag &= ~CSTOPB;
            break;
        case 2:
            option.c_cflag |= CSTOPB;
            break;
        
        default:
            fprintf(stderr, "Unsupported stop bits\n");
            return -1;
            break;
        }

    /*设置校验方式*/
    switch(parity) {
        case 'n':
        case 'N':  // 无校验
            option.c_cflag &= ~PARENB;  // 禁用校验位
            option.c_iflag &= ~INPCK;   // 禁用奇偶校验检查
            break;

        case 'e':
        case 'E':  // 偶校验
            option.c_cflag |= PARENB;   // 启用校验位
            option.c_cflag &= ~PARODD;  // 设置为偶校验
            option.c_iflag |= INPCK;    // 启用奇偶校验检查
            break;

        case 'o':
        case 'O':  // 奇校验
            option.c_cflag |= PARENB;   // 启用校验位
            option.c_cflag |= PARODD;   // 设置为奇校验
            option.c_iflag |= INPCK;    // 启用奇偶校验检查
            break;

        default:
            fprintf(stderr, "Unsupported parity\n");
            return -1;
    }


    
    /* 其他通用设置 */
    option.c_cflag |= (CLOCAL | CREAD); /* 启用接收器，忽略调制解调器控制线 */
    option.c_oflag = 0;         /* 原始输出模式 */
    option.c_lflag = 0;         /* 非规范模式，禁用回显和信号 */
    option.c_cc[VMIN] = 1;      /* 读取最小字符数 */
    option.c_cc[VTIME] = 1; 
    if(tcsetattr(fd, TCSANOW, &option) != 0) {
        perror("tcsetattr COM error");
        return -1;
    }

    return 0;


}


int flush_dev(int fd)
{
    if (tcflush(fd, TCIOFLUSH) != 0) {
        perror("tcflush error");
        return -1;
    }
    return 0;
}



/*读*/
int read_dev(int fd, char *buffer, int size) 
{
    int len = 0;
    len = read(fd, buffer, size);
    if (len < 0) {
        perror("Read COM error");
        return -1;
    }
    return len;
}



/*写入串口数据*/
int write_dev(int fd, const char *buffer, int size)
{
    int len = 0;
    len = write(fd, buffer, size);
    if (len < 0) {
        perror("Write COM error");
        return -1;
    }

    if (tcdrain(fd) < 0) {
        perror("tcdrain error");
        return -1;
    }

    return len;
}

int check_dev(int fd, int timeout_sec)
{
    fd_set read_fds;
    struct timeval timeout;
    int ret;

    FD_ZERO(&read_fds);
    FD_SET(fd, &read_fds);

    timeout.tv_sec = timeout_sec;
    timeout.tv_usec = 0;

    /* 使用 select 来检查是否有数据可读 */
    ret = select(fd + 1, &read_fds, NULL, NULL, &timeout);
    
    if (ret == -1) {
        perror("select error");
        return -1;  
    }
    else if (ret == 0) {
        //printf("No data available in the buffer\n");
        return 0;  // 无数据
    }
    else {
        //printf("Data is available in the buffer\n");
        return 1;  // 有数据
    }
}





